package com.example.su_shi_tan_bas_lib.album.view.editor_img.view

import android.animation.Animator
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.Log
import android.view.*
import android.widget.FrameLayout
import com.example.su_shi_tan_bas_lib.BuildConfig


import com.example.su_shi_tan_bas_lib.album.view.editor_img.core.IMGImage
import com.example.su_shi_tan_bas_lib.album.view.editor_img.core.IMGMode
import com.example.su_shi_tan_bas_lib.album.view.editor_img.core.IMGPath
import com.example.su_shi_tan_bas_lib.album.view.editor_img.core.IMGText
import com.example.su_shi_tan_bas_lib.album.view.editor_img.core.anim.IMGHomingAnimator
import com.example.su_shi_tan_bas_lib.album.view.editor_img.core.homing.IMGHoming
import com.example.su_shi_tan_bas_lib.album.view.editor_img.core.sticker.IMGSticker
import com.example.su_shi_tan_bas_lib.album.view.editor_img.core.sticker.IMGStickerPortrait
import kotlin.math.roundToInt


class AlbumEditorImg @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) :
    FrameLayout(context, attrs, defStyleAttr), MoveAdapter.OnScrollListener,
    ScaleGestureDetector.OnScaleGestureListener, ValueAnimator.AnimatorUpdateListener,
    Animator.AnimatorListener, Runnable, IMGStickerPortrait.Callback {

    companion object {
        private const val TAG = "AlbumEditorImg"
    }

    private var mPreMode = IMGMode.NONE

    private val mImage = IMGImage()

    private var mGDetector: GestureDetector? = null

    private var mSGDetector: ScaleGestureDetector? = null

    private var mHomingAnimator: IMGHomingAnimator? = null

    private val mPen = Pen()

    private var mPointerCount = 0

    private val mDoodlePaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)

    private val mMosaicPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)


    init {

        // 涂鸦画刷
        mDoodlePaint.style = Paint.Style.STROKE
        mDoodlePaint.strokeWidth = IMGPath.BASE_DOODLE_WIDTH
        mDoodlePaint.color = Color.RED
        mDoodlePaint.pathEffect = CornerPathEffect(IMGPath.BASE_DOODLE_WIDTH)
        mDoodlePaint.strokeCap = Paint.Cap.ROUND
        mDoodlePaint.strokeJoin = Paint.Join.ROUND

        // 马赛克画刷
        mMosaicPaint.style = Paint.Style.STROKE
        mMosaicPaint.strokeWidth = IMGPath.BASE_MOSAIC_WIDTH
        mMosaicPaint.color = Color.BLACK
        mMosaicPaint.pathEffect = CornerPathEffect(IMGPath.BASE_MOSAIC_WIDTH)
        mMosaicPaint.strokeCap = Paint.Cap.ROUND
        mMosaicPaint.strokeJoin = Paint.Join.ROUND

        mPen.mode = mImage.mode
        mGDetector = GestureDetector(context, MoveAdapter(this))
        mSGDetector = ScaleGestureDetector(context, this)

//        willNotDraw()
        setWillNotDraw(false)

    }


    fun setBitmap(image: Bitmap?) {
        mImage.setBitmap(cloneBitmap(image))
        invalidate()
    }

    private fun cloneBitmap(resource: Bitmap?): Bitmap? {
        if (resource == null) return null
        val bitmap2 =
            Bitmap.createBitmap(resource.width, resource.height, resource.config)

        // 拿着可以被修改的图片创建一个画布.
        val canvas = Canvas(bitmap2)
        val paint = Paint()
        canvas.drawBitmap(resource, Matrix(), paint)
        return bitmap2
    }

    fun setMode(mode: IMGMode?) {
        // 保存现在的编辑模式
        mPreMode = mImage.mode

        // 设置新的编辑模式
        mImage.mode = mode
        mPen.mode = mImage.mode

        // 矫正区域
        onHoming()
    }

    /**
     * 是否真正修正归位
     */
    private fun isHoming(): Boolean {
        return mHomingAnimator?.isRunning ?: false
    }

    private fun onHoming() {
        invalidate()
        stopHoming()
        startHoming(
            mImage.getStartHoming(scrollX.toFloat(), scrollY.toFloat()),
            mImage.getEndHoming(scrollX.toFloat(), scrollY.toFloat())
        )
    }

    private fun startHoming(sHoming: IMGHoming, eHoming: IMGHoming) {
        mHomingAnimator = IMGHomingAnimator()
        mHomingAnimator?.addUpdateListener(this)
        mHomingAnimator?.addListener(this)

        mHomingAnimator?.setHomingValues(sHoming, eHoming)
        mHomingAnimator?.start()
    }

    private fun stopHoming() {
        mHomingAnimator?.cancel()
    }

    fun doRotate() {
        if (!isHoming()) {
            mImage.rotate(-90)
            onHoming()
        }
    }

    fun resetClip() {
        mImage.resetClip()
        onHoming()
    }

    fun doClip() {
        mImage.clip(scrollX.toFloat(), scrollY.toFloat())
        setMode(mPreMode)
        onHoming()
    }

    fun cancelClip() {
        mImage.toBackupClip()
        setMode(mPreMode)
    }

    fun setPenColor(color: Int) {
        mPen.color = color
    }

    fun isDoodleEmpty(): Boolean {
        return mImage.isDoodleEmpty
    }

    fun undoDoodle() {
        mImage.undoDoodle()
        invalidate()
    }

    fun isMosaicEmpty(): Boolean {
        return mImage.isMosaicEmpty
    }

    fun undoMosaic() {
        mImage.undoMosaic()
        invalidate()
    }

    fun getMode(): IMGMode? {
        return mImage.mode
    }

    fun isClipping() = getMode() === IMGMode.CLIP

//    override fun dispatchDraw(canvas: Canvas?) {
//        super.dispatchDraw(canvas)
//        onDrawImages(canvas)
//    }

//    override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
//        if (isClipping() && keyCode == KeyEvent.KEYCODE_BACK) {
//            cancelClip()
//            return true
//        }
//        return false
//    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        onDrawImages(canvas)
    }

    private fun onDrawImages(canvas: Canvas?) {
        canvas?.save()

        // clip 中心旋转
        val clipFrame = mImage.clipFrame
        canvas?.rotate(mImage.rotate, clipFrame.centerX(), clipFrame.centerY())

        // 图片
        mImage.onDrawImage(canvas)

        // 马赛克
        if (!mImage.isMosaicEmpty || mImage.mode == IMGMode.MOSAIC && !mPen.isEmpty) {
            val count = mImage.onDrawMosaicsPath(canvas)
            if (mImage.mode == IMGMode.MOSAIC && !mPen.isEmpty) {
                mDoodlePaint.strokeWidth = IMGPath.BASE_MOSAIC_WIDTH
                canvas?.save()
                val frame = mImage.clipFrame
                canvas?.rotate(-mImage.rotate, frame.centerX(), frame.centerY())
                canvas?.translate(scrollX.toFloat(), scrollY.toFloat())
                canvas?.drawPath(mPen.path, mDoodlePaint)
                canvas?.restore()
            }
            mImage.onDrawMosaic(canvas, count)
        }

        // 涂鸦
        mImage.onDrawDoodles(canvas)
        if (mImage.mode == IMGMode.DOODLE && !mPen.isEmpty) {
            mDoodlePaint.color = mPen.color
            mDoodlePaint.strokeWidth = IMGPath.BASE_DOODLE_WIDTH * mImage.scale
            canvas?.save()
            val frame = mImage.clipFrame
            canvas?.rotate(-mImage.rotate, frame.centerX(), frame.centerY())
            canvas?.translate(scrollX.toFloat(), scrollY.toFloat())
            canvas?.drawPath(mPen.path, mDoodlePaint)
            canvas?.restore()
        }

        // TODO
        if (mImage.isFreezing) {
            // 文字贴片
            mImage.onDrawStickers(canvas)
        }

        mImage.onDrawShade(canvas)

        canvas!!.restore()

        // TODO
        if (!mImage.isFreezing) {
            // 文字贴片
            mImage.onDrawStickerClip(canvas)
            mImage.onDrawStickers(canvas)
        }

        // 裁剪
        if (mImage.mode == IMGMode.CLIP) {
            canvas.save()
            canvas.translate(scrollX.toFloat(), scrollY.toFloat())
            mImage.onDrawClip(canvas, scrollX.toFloat(), scrollY.toFloat())
            canvas.restore()
        }
    }

    fun saveBitmap(): Bitmap? {
        mImage.stickAll()
        val scale = 1f / mImage.scale
        val frame = RectF(mImage.clipFrame)

        // 旋转基画布
        val m = Matrix()
        m.setRotate(mImage.rotate, frame.centerX(), frame.centerY())
        m.mapRect(frame)

        // 缩放基画布
        m.setScale(scale, scale, frame.left, frame.top)
        m.mapRect(frame)
        val bitmap = Bitmap.createBitmap(
            frame.width().roundToInt(),
            frame.height().roundToInt(), Bitmap.Config.ARGB_8888
        )
        val canvas = Canvas(bitmap)

        // 平移到基画布原点&缩放到原尺寸
        canvas.translate(-frame.left, -frame.top)
        canvas.scale(scale, scale, frame.left, frame.top)
        onDrawImages(canvas)
        return bitmap
    }

    override fun onLayout(
        changed: Boolean,
        left: Int,
        top: Int,
        right: Int,
        bottom: Int
    ) {
        super.onLayout(changed, left, top, right, bottom)
        if (changed) {
//            val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(context)
            mImage.onWindowChanged((right - left).toFloat(), (bottom - top).toFloat())
        }
    }

    private fun <V> addStickerView(
        stickerView: V?,
        params: LayoutParams?
    ) where V : View?, V : IMGSticker? {
        if (stickerView != null) {
            addView(stickerView, params)
            stickerView.registerCallback(this)
            mImage.addSticker(stickerView)
        }
    }

    fun addStickerText(text: IMGText?) {
        val textView = IMGStickerTextView(context)
        textView.text = text
        val layoutParams = LayoutParams(
            LayoutParams.WRAP_CONTENT,
            LayoutParams.WRAP_CONTENT
        )

        // Center of the drawing window.
        layoutParams.gravity = Gravity.CENTER
        textView.x = scrollX.toFloat()
        textView.y = scrollY.toFloat()
        addStickerView(textView, layoutParams)
    }

    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        return if (ev.actionMasked == MotionEvent.ACTION_DOWN) {
            onInterceptTouch(ev) || super.onInterceptTouchEvent(ev)
        } else super.onInterceptTouchEvent(ev)
    }

    private fun onInterceptTouch(event: MotionEvent?): Boolean {
        if (isHoming()) {
            stopHoming()
            return true
        } else if (mImage.mode == IMGMode.CLIP) {
            return true
        }
        return false
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.actionMasked) {
            MotionEvent.ACTION_DOWN -> removeCallbacks(this)
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> postDelayed(this, 1200)
        }
        return onTouch(event)
    }

    private fun onTouch(event: MotionEvent): Boolean {
        if (isHoming()) {
            // Homing
            return false
        }
        mPointerCount = event.pointerCount
        var handled = mSGDetector!!.onTouchEvent(event)
        val mode = mImage.mode
        handled = if (mode == IMGMode.NONE || mode == IMGMode.CLIP) {
            handled or onTouchNONE(event)
        } else if (mPointerCount > 1) {
            onPathDone()
            handled or onTouchNONE(event)
        } else {
            handled or onTouchPath(event)
        }
        when (event.actionMasked) {
            MotionEvent.ACTION_DOWN -> mImage.onTouchDown(event.x, event.y)
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                mImage.onTouchUp(scrollX.toFloat(), scrollY.toFloat())
                onHoming()
            }
        }
        return handled
    }


    private fun onTouchNONE(event: MotionEvent): Boolean {
        return mGDetector!!.onTouchEvent(event)
    }

    private fun onTouchPath(event: MotionEvent): Boolean {
        when (event.actionMasked) {
            MotionEvent.ACTION_DOWN -> return onPathBegin(event)
            MotionEvent.ACTION_MOVE -> return onPathMove(event)
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> return mPen.isIdentity(
                event.getPointerId(
                    0
                )
            ) && onPathDone()
        }
        return false
    }

    private fun onPathBegin(event: MotionEvent): Boolean {
        mPen.reset(event.x, event.y)
        mPen.setIdentity(event.getPointerId(0))
        return true
    }

    private fun onPathMove(event: MotionEvent): Boolean {
        if (mPen.isIdentity(event.getPointerId(0))) {
            mPen.lineTo(event.x, event.y)
            invalidate()
            return true
        }
        return false
    }

    private fun onPathDone(): Boolean {
        if (mPen.isEmpty) {
            return false
        }
        mImage.addPath(mPen.toPath(), scrollX.toFloat(), scrollY.toFloat())
        mPen.reset()
        invalidate()
        return true
    }

    override fun onScroll(dx: Float, dy: Float): Boolean {
        val homing =
            mImage.onScroll(scrollX.toFloat(), scrollY.toFloat(), -dx, -dy)
        if (homing != null) {
            toApplyHoming(homing)
            return true
        }
        return onScrollTo(scrollX + dx.roundToInt(), scrollY + dy.roundToInt())
    }

    private fun onScrollTo(x: Int, y: Int): Boolean {
        if (scrollX != x || scrollY != y) {
            scrollTo(x, y)
            return true
        }
        return false
    }


    private fun toApplyHoming(homing: IMGHoming?) {
        mImage.scale = homing?.scale ?: 0f
        mImage.rotate = homing?.rotate ?: 0f
        if (!onScrollTo(homing?.x?.roundToInt() ?: 0, homing?.y?.roundToInt() ?: 0)) {
            invalidate();
        }

    }

    override fun onScaleBegin(p0: ScaleGestureDetector?): Boolean {
        if (mPointerCount > 1) {
            mImage.onScaleBegin()
            return true
        }
        return false
    }

    override fun onScaleEnd(p0: ScaleGestureDetector?) {
        mImage.onScaleEnd()
    }

    override fun onScale(detector: ScaleGestureDetector?): Boolean {
        if (mPointerCount > 1) {
            mImage.onScale(
                detector?.scaleFactor ?: 0f,
                scrollX + (detector?.focusX ?: 0f),
                scrollY + (detector?.focusY ?: 0f)
            )
            invalidate()
            return true
        }
        return false
    }

    override fun onAnimationUpdate(p0: ValueAnimator?) {
        mImage.onHoming(p0?.animatedFraction ?: 0f)
        toApplyHoming(p0?.animatedValue as IMGHoming?)
    }

    override fun onAnimationRepeat(p0: Animator?) {

    }

    override fun onAnimationEnd(p0: Animator?) {
        if (BuildConfig.DEBUG) {
            Log.d(TAG, "onAnimationEnd");
        }
        val scrollX = this.scrollX.toFloat()
        val scrollY = this.scrollY.toFloat()

        if (mImage.onHomingEnd(scrollX, scrollY, mHomingAnimator?.isRotate == true)) {
            toApplyHoming(mImage.clip(scrollX, scrollY))
        }
    }

    override fun onAnimationCancel(p0: Animator?) {
        if (BuildConfig.DEBUG) {
            Log.d(TAG, "onAnimationCancel");
        }
        mImage.onHomingCancel(mHomingAnimator?.isRotate == true)
    }

    override fun onAnimationStart(p0: Animator?) {
        if (BuildConfig.DEBUG) {
            Log.d(TAG, "onAnimationStart");
        }
        mImage.onHomingStart(mHomingAnimator?.isRotate == true)
    }

    override fun run() {
        // 稳定触发
        if (!onSteady()) {
            postDelayed(this, 500);
        }
    }

    private fun onSteady(): Boolean {
        if (BuildConfig.DEBUG) {
            Log.d(TAG, "onSteady: isHoming=" + isHoming())
        }
        if (!isHoming()) {
            mImage.onSteady(scrollX.toFloat(), scrollY.toFloat())
            onHoming()
            return true
        }
        return false
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        removeCallbacks(this)
        mImage.release()
    }


    override fun <V> onRemove(stickerView: V): Boolean where V : View?, V : IMGSticker? {
        mImage.onRemoveSticker(stickerView)
        stickerView!!.unregisterCallback(this)
        val parent = stickerView.parent
        if (parent != null) {
            (parent as ViewGroup).removeView(stickerView)
        }
        return true
    }

    override fun <V> onDismiss(stickerView: V) where V : View?, V : IMGSticker? {
        mImage.onDismiss(stickerView)
        invalidate()
    }

    override fun <V> onShowing(stickerView: V) where V : View?, V : IMGSticker? {
        mImage.onShowing(stickerView);
        invalidate()
    }


}